package com.teemor.common.bean.utils.redis;


import com.teemor.common.bean.utils.SerializeUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;

import java.util.Objects;


public class RedisUtils {
    private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);


    private static volatile RedisClient redisClient;

    /**
     * 初始化RedisUtils实例，利用双锁锁定实现单例
     *
     * @param host
     * @param port
     * @param auth
     */
    public static void init(String host, Integer port, String auth) {
        if (redisClient == null) {
            synchronized (String.class) {
                if (redisClient == null) {
                    redisClient = new RedisClient(host, port, auth);
                }
            }
        }

    }

    public static void setString(String key, String value) {
        if (isNull(key, value))
            return;
        byte[] k = key.getBytes();
        byte[] v = value.getBytes();
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.set(k, v);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static void setObject(String key, Object value) {
        if (isNull(key, value))
            return;
        byte[] k = key.getBytes();
        byte[] v = SerializeUtils.serialize(value);
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.set(k, v);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static void setObjectAndTime(String key, Object value, int seconds) {
        if (isNull(key, value))
            return;
        byte[] k = key.getBytes();
        byte[] v = SerializeUtils.serialize(value);
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.setex(k, seconds, v);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static void setStringAndTime(String key, String value, int seconds) {
        if (isNull(key, value))
            return;
        byte[] k = key.getBytes();
        byte[] v = value.getBytes();
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.setex(k, seconds, v);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static boolean setObjectNx(String key, Object value) {
        if (isNull(key, value))
            return false;
        byte[] k = key.getBytes();
        byte[] v = SerializeUtils.serialize(value);
        Jedis jedis = redisClient.getJedis();
        try {
            return jedis.setnx(k, v) == 1;
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static boolean setStringNx(String key, String value) {
        if (isNull(key, value))
            return false;
        byte[] k = key.getBytes();
        byte[] v = value.getBytes();
        Jedis jedis = redisClient.getJedis();
        try {
            return jedis.setnx(k, v) == 1;
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static void setTime(String key, int second) {
        if (StringUtils.isBlank(key))
            return;
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.expire(k, second);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static String getString(String key) {
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();

        byte[] v;
        String value;
        try {
            v = jedis.get(k);
            value = v == null ? null : new String(v);
        } finally {
            redisClient.releaseResource(jedis);
        }
        return value;

    }

    public static Object getObject(String key) {
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();
        Object value;
        try {
            value = SerializeUtils.deserialize(jedis.get(k));
        } finally {
            redisClient.releaseResource(jedis);
        }
        return value;
    }

    public static void del(String key) {
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.del(k);
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    public static Long ttl(String key) {
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();
        Long time = 0L;
        try {
            time = jedis.ttl(k);
        } finally {
            redisClient.releaseResource(jedis);
        }
        return time;
    }

    public static Boolean exists(String key) {
        byte[] k = key.getBytes();
        Jedis jedis = redisClient.getJedis();
        Boolean bl = false;
        try {
            bl = jedis.exists(k);
        } finally {
            redisClient.releaseResource(jedis);
        }
        return bl;
    }

    public static void flushDB() {
        Jedis jedis = redisClient.getJedis();
        try {
            jedis.flushDB();
        } finally {
            redisClient.releaseResource(jedis);
        }
    }

    private static boolean isNull(Object key, Object value) {
        return key == null || value == null;
    }

    /**
     * @Function: com.teemor.common.bean.utils.redis.RedisUtils.limit
     * @Description: 简单限流，对同一个key在限定时间内的操作次数进行限制
     * @param: key 具有业务意义的独立key
     * @param: seconds 限流持续时间范围，单位 秒
     * @param: count 限流时间内最大次数
     * @return: boolean 是否以达到最大限制次数
     * @version: v1.0.0
     * @author: zhoulk
     * @date: 2020/3/3 17:25
     * <p>
     * Modification History:
     * Date             Author      Version            Description
     * ---------------------------------------------------------*
     * 2020/3/3 17:25    zhoulk      v1.0.0             修改原因
     */
    public static int simpleLimit(String key, Integer seconds, Integer count) {
        if (!exists(key)) {
            setObjectAndTime(key, 1, seconds);
            return count - 1;
        }
        Integer realCount = (Integer) getObject(key);
        if (realCount >= count) {
            return -1;
        } else {
            setObjectAndTime(key, realCount + 1, ttl(key).intValue());
        }
        return count - realCount - 1;
    }
}
